package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;

/**
 * @author atguigu-mqx
 */
@Component
public class SeckillReceiver {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    protected SeckillGoodsService seckillGoodsService;

    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void importToRedis(String msg, Message message, Channel channel){
        try {
            System.out.println(msg);
            //  获取到当天秒杀的商品数据！ start_time = new date(); status = 1 ;stock_count>0;
            //  select * from seckill_goods where status = 1 and stock_count>0 and date_format(start_time,'%Y-%m-%d') = date_format(now(),'%Y-%m-%d');
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            seckillGoodsQueryWrapper.eq("status","1").gt("stock_count",0);
            seckillGoodsQueryWrapper.eq("date_format(start_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));
            List<SeckillGoods> seckillGoodsList = this.seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
            //  需要将数据放入缓存！ 考虑数据类型，以及缓存的key！ Hash 或 String!
            if (!CollectionUtils.isEmpty(seckillGoodsList)){
                //  循环遍历
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    //  hash : hset key field value  hget key field
                    String seckillKey = RedisConst.SECKILL_GOODS;
                    //  存储数据！
                    Boolean result = this.redisTemplate.opsForHash().hasKey(seckillKey, seckillGoods.getSkuId().toString());
                    if (result){
                        //  当前这个商品在缓存中存在！
                        //  return; //   停止这个方法！
                        //  break;  //  跳出循环！
                        continue;   //  结束本次循环！
                    }
                    //  保存数据
                    this.redisTemplate.opsForHash().put(seckillKey,seckillGoods.getSkuId().toString(),seckillGoods);

                    //  如何控制库存超买？  10件商品！ 将商品的剩余库存数 写入缓存！ 使用队列 list 数据类型来存储！
                    //  list  lpush/rpush  rpop/lpop;
                    for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                        //  key = seckill:stock:skuId  value = skuId
                        this.redisTemplate.opsForList().leftPush(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId().toString(),seckillGoods.getSkuId().toString());

                        //  this.redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId().toString()).leftPush(seckillGoods.getSkuId().toString());
                    }
                    //  只要商品已发布那么这个商品的状态位就应该是1
                    //  skuId:1  或  skuId:0     1:表示有库存  0:表示没有库存  发布： publish seckillpush 24:1
                    this.redisTemplate.convertAndSend("seckillpush",seckillGoods.getSkuId()+":1");
                    //  如何 订阅：subscribe seckillpush?
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //  确认消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //  监听用户秒杀的是哪款商品!
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = {MqConst.ROUTING_SECKILL_USER}
    ))
    public void seckillUser(UserRecode userRecode,Message message, Channel channel){
        try {
            //  业务处理！ skuId userId
            if (userRecode!=null){
                //  保存预下单数据！ 将 skuId userId 从队列中读取出来，然后能否秒杀到商品... ; 校验通过，将数据放入缓存！
                seckillGoodsService.seckillOrder(userRecode.getSkuId(),userRecode.getUserId());
            }

        }catch (Exception e){
            e.printStackTrace();
        }

        //  消息确认：
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    //  秒杀结束清空缓存！
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_18,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_18}
    ))
    public void clearRedisData(Message message,Channel channel){
        try {
            //  获取到秒杀结束的数据！ end_time < new date(); status = 1;
            QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            seckillGoodsQueryWrapper.eq("status","1");
            seckillGoodsQueryWrapper.le("end_time",new Date());
            List<SeckillGoods> seckillGoodsList = this.seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);

            //  删除数据：
            //  当前用户在缓存中存在！如果强制删除就需要有用户Id，那么可以不删除，因为这个key 有过期时间！
            //          Boolean result = this.redisTemplate.opsForValue().setIfAbsent(seckillUserKey, skuId, RedisConst.SECKILL__TIMEOUT, TimeUnit.SECONDS);
            //  this.redisTemplate.delete("seckill:user:2");

            //  秒杀只发布一件商品 ：seckill:goods
            //            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS);
            //            //  商品的剩余库存：seckill:stock:24 如果只有一件商品
            //            this.redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX+seckillGoodsList.get(0).getSkuId());
            //            //  删除用户真正下过订单的数据! 获取到用户Id
            //            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);
            //            //  总体商品！如果只有一件商品则直接执行删除！
            //            this.redisTemplate.delete(RedisConst.SECKILL_GOODS);

            //  如果秒杀中运行有多个商品！
            for (SeckillGoods seckillGoods : seckillGoodsList) {
                //  删除秒杀商品数据
                this.redisTemplate.boundHashOps(RedisConst.SECKILL_GOODS).delete(seckillGoods.getSkuId().toString());
                //  删除库存数据
                this.redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId());
            }

            //  结束时，删除秒杀key！
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);
            //  预下单商品 ：在秒杀的时候，用户有可能到秒杀成功就结束了！
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS);

            //  数据库呢; 更改数据库表中对应的这个状态！
            //  status = 1 ; 审核通过 ;  status = 2 ;  秒杀结束
            SeckillGoods seckillGoods = new SeckillGoods();
            seckillGoods.setStatus("2");
            seckillGoodsMapper.update(seckillGoods,seckillGoodsQueryWrapper);

        } catch (Exception e) {
            e.printStackTrace();
        }
        //  手动确认！
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

}
